﻿using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Microsoft.Playwright;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;

namespace Jy.Abp.Playwright;

public class PlaywrightProvider : ISingletonDependency, IPlaywrightProvider
{
    private const string _browerScopeKey = "BrowserScope";

    private readonly PlaywrightOptions _options;
    private readonly Lazy<Task<IPlaywright>> _playwright;
    private readonly Lazy<Task<IBrowser>> _defaultBrowser;
    private readonly IAmbientScopeProvider<BrowserScopeItem> _browserScopeProvider;
    public PlaywrightProvider(
        IOptions<PlaywrightOptions> options,
        IAmbientScopeProvider<BrowserScopeItem> browserScopeProvider)
    {
        this._options = options.Value;
        this._browserScopeProvider = browserScopeProvider;

        this._playwright = new Lazy<Task<IPlaywright>>(Microsoft.Playwright.Playwright.CreateAsync, true);
        this._defaultBrowser = new Lazy<Task<IBrowser>>(() => this.LaunchBrowserAsync(), true);
    }

    /// <summary>
    /// 配置
    /// </summary>
    public PlaywrightOptions Options => this._options;

    /// <summary>
    /// 创建一个新的浏览器对像
    /// </summary>
    private async Task<IBrowser> LaunchBrowserAsync(
        string browserType = null,
        BrowserTypeLaunchOptions options = null)
    {
        browserType = browserType ?? this._options.DefaultBrowserType;
        options = options ?? this.Options.DefaultLaunchOptions;

        var playwright = await this.GetPlaywrightAsync();
        var bt = browserType.ToLower() switch
        {
            "chromium" => playwright.Chromium,
            "firefox" => playwright.Firefox,
            "webkit" => playwright.Webkit,
            _ => throw new ArgumentException($"Unsupported browser type: {browserType}")
        };
        return await bt.LaunchAsync(options);
    }

    /// <summary>
    /// 获取playwright对像
    /// </summary>
    public async Task<IPlaywright> GetPlaywrightAsync()
    {
        return await this._playwright.Value;
    }

    public IAsyncDisposable UseBrowser(
        string browserType = null,
        BrowserTypeLaunchOptions options = null)
    {
        var item = new BrowserScopeItem(browserType, options, this.LaunchBrowserAsync);
        var scope = this._browserScopeProvider.BeginScope(_browerScopeKey, item);
        return new AsyncDisposeFunc(
            () =>
            {
                scope.Dispose();
                return Task.Run(() => item.DisposeAsync());
            });
    }

    public async Task<IBrowser> GetBrowserAsync()
    {
        var scope = this._browserScopeProvider.GetValue(_browerScopeKey);
        if (scope != null)
            return await scope.GetBrowserAsync();
        return await this._defaultBrowser.Value;
    }

    public async Task<IBrowserContext> GetContextAsync(BrowserNewContextOptions options = null)
    {
        var browser = await this.GetBrowserAsync();
        options = options ?? this._options.DefaultContextOptions;
        return await browser.NewContextAsync(options);
    }

    public async Task<IPage> GetPageAsync(BrowserNewPageOptions options = null)
    {
        var browser = await this.GetBrowserAsync();
        options = options ?? this._options.DefaultPageOptions;
        return await browser.NewPageAsync(options);
    }

    public async ValueTask DisposeAsync()
    {
        if (this._defaultBrowser.IsValueCreated)
            await (await this._defaultBrowser.Value).DisposeAsync();
        (await this._playwright.Value).Dispose();
    }
}
